package com.googlecode.openrtp.grizzly.rtp;

import com.google.inject.Singleton;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import com.googlecode.openrtp.grizzly.ThreadResources;
import com.googlecode.openrtp.grizzly.session.PacketGenerator;
import com.googlecode.openrtp.grizzly.session.SessionContextListener;
import com.googlecode.openrtp.spi.RtpSession;
import com.googlecode.openrtp.spi.SessionKeys;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.LockSupport;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author jocke
 */
@Singleton
public final class RtpManager implements SessionContextListener {

    private static final Map<Long, RtpGeneratePacketNotifier> RUNNING_SESSIONS = new ConcurrentHashMap<>();

    private static final Logger LOG = Logger.getLogger(RtpManager.class.getName());

    private final ThreadResources threadResources;

    @Inject
    public RtpManager(ThreadResources threadResources) {
        this.threadResources = threadResources;
    }

    @Override
    public void sessionAdded(RtpSession session) {
        session.set(SessionKeys.NEXT_RTP_TRANSMISSION, getNextTransmission(session));
        RtpGeneratePacketNotifier notifier = new RtpGeneratePacketNotifier(PacketGenerator.class.cast(session), session);
        threadResources.getCachedExecutorService().submit(notifier);
        RUNNING_SESSIONS.put(session.lookup(SessionKeys.SSRC), notifier);
    }

    @Override
    public void sessionRemoved(RtpSession session) {
        RtpGeneratePacketNotifier remove = RUNNING_SESSIONS.remove(session.lookup(SessionKeys.SSRC));
        if (remove != null) {
            remove.setRunning(false);
        }
    }

    private long getNextTransmission(RtpSession session) {
        return System.currentTimeMillis() + session.lookup(SessionKeys.RTP_PACKET_RATE);
    }

    private final class RtpGeneratePacketNotifier implements Runnable {

        private volatile boolean running = true;

        private final PacketGenerator generator;

        private final RtpSession session;

        public RtpGeneratePacketNotifier(PacketGenerator generator, RtpSession session) {
            this.generator = generator;
            this.session = session;
        }

        public boolean isRunning() {
            return running;
        }

        public void setRunning(boolean running) {
            this.running = running;
        }

        @Override
        public void run() {
            while (running) {
                try {
                    long start = System.nanoTime();
                    generator.generateRtpPacket();
                    long dur = System.nanoTime() - start;
                    session.set(SessionKeys.NEXT_RTP_TRANSMISSION, getNextTransmission(session));
                    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(session.lookup(SessionKeys.RTP_PACKET_RATE)) - dur);
                } catch (Exception e) {
                    LOG.log(Level.SEVERE, "Failed to generate packet will exit", e);
                    running = false;
                }
            }
        }
    }
}
